// 将以上图示用代码实现
// updateChildren.js

import patchVnode from "./patchVnode"
import createElement from './createElement'
// 判断是否是同一个虚拟节点
function checkSameVnode(a, b) {
    return a.sel == b.sel && a.key == b.key
}
export default function updateChildren(parentElm, oldCh, newCh) {
    console.log(oldCh, newCh)
    // 旧前
    let oldStartIdx = 0
    // 新前
    let newStartIdx = 0
    // 旧后
    let oldEndIdx = oldCh.length - 1
    // 新后
    let newEndIdx = newCh.length - 1

    // 旧前节点
    let oldStartVnode = oldCh[0]
    // 旧后节点
    let oldEndVnode = oldCh[oldEndIdx]
    // 新前节点
    let newStartVnode = newCh[0]
    // 新后节点
    let newEndVnode = newCh[newEndIdx]
    let keyMap = null
    while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
        // 首先判断不是四种情况是否命中，而是要掠过已经加undefined标记的节点
        if (oldStartVnode == null || oldCh[oldStartIdx] == undefined) {
            oldStartVnode = oldCh[++oldStartIdx]
        } else if (oldEndVnode == null || oldCh[oldEndIdx] == undefined) {
            oldEndVnode = oldCh[--oldEndIdx]
        } else if (newStartVnode == null || newCh[newStartIdx] == undefined) {
            newStartVnode = newCh[++newStartIdx]
        } else if (newEndVnode == null || newCh[newEndIdx] == undefined) {
            newEndVnode = newCh[++newEndIdx]
        }
        // 判断新旧是否是同一个虚拟节点 
        else if (checkSameVnode(oldStartVnode, newStartVnode)) {
            // 新前与旧前
            console.log("新前与旧前 命中");
            // 精细化比较两个节点 oldStartVnode现在和newStartVnode一样了
            patchVnode(oldStartVnode, newStartVnode);
            // 移动指针，改变指针指向的节点，这表示这两个节点都处理（比较）完了
            oldStartVnode = oldCh[++oldStartIdx];
            newStartVnode = newCh[++newStartIdx];
        } else if (checkSameVnode(oldEndVnode, newEndVnode)) {
            // 新后与旧后
            console.log("新后与旧后 命中");
            patchVnode(oldEndVnode, newEndVnode);
            oldEndVnode = oldCh[--oldEndIdx];
            newEndVnode = newCh[--newEndIdx];
        } else if (checkSameVnode(oldStartVnode, newEndVnode)) {
            // 新后与旧前
            console.log("新后与旧前 命中");
            patchVnode(oldStartVnode, newEndVnode);
            // 当新后与旧前命中的时候，此时要移动节点。移动 新后（旧前） 指向的这个节点到老节点的 旧后的后面 也就是旧后下一个兄弟之前
            // 移动节点：只要插入一个已经在DOM树上 的节点，就会被移动
            parentElm.insertBefore(oldStartVnode.elm, oldEndVnode.elm.nextSibling);
            oldStartVnode = oldCh[++oldStartIdx];
            newEndVnode = newCh[--newEndIdx];
        } else if (checkSameVnode(oldEndVnode, newStartVnode)) {
            // 新前与旧后
            console.log("新前与旧后 命中");
            patchVnode(oldEndVnode, newStartVnode);
            // 当新前与旧后命中的时候，此时要移动节点。移动 新前（旧后） 指向的这个节点到老节点的 旧前的前面
            // 移动节点：只要插入一个已经在DOM树上的节点，就会被移动
            parentElm.insertBefore(oldEndVnode.elm, oldStartVnode.elm);
            oldEndVnode = oldCh[--oldEndIdx];
            newStartVnode = newCh[++newStartIdx];
        } else {
            // 四种都没有命中的情况
            // 制作keyMap一个映射对象，这样就不用每次都遍历老对象了
            if (!keyMap) {
                keyMap = {}
                // 从oldStartInx开始，到oldEndIdx结束，创建keyMap映射的对象
                for (let i = oldStartIdx; i <= oldEndIdx; i++) {
                    const key = oldCh[i].key
                    if (key !== undefined) {
                        keyMap[key] = i
                    }
                }
            }
            console.log(keyMap)
            // 寻找当前这项(newStartIdx) 这项在keyMap中的映射的位置序号
            const idxInOld = keyMap[newStartVnode.key]
            console.log(idxInOld)
            if (idxInOld == undefined) {
                // 判断，如果idxInOld是undefined表示他是全新的项
                // 被加入项（就是newStartVnode这项）现不是真正的DOM节点
                parentElm.insertBefore(createElement(newStartVnode), oldStartVnode.elm)
            } else {
                // 如果不是undefined，不是全新的项，而是要移动
                const elmToMove = oldCh[idxInOld]
                patchVnode(elmToMove, newStartVnode)
                // 把这项设置为undefined，表示我已经处理完了这项了
                oldCh[idxInOld] = undefined
                // 移动，调用insertBefore也可以实现移动
                parentElm.insertBefore(elmToMove.elm, oldStartVnode.elm)

            }
            // 指针指移新的头
            newStartVnode = newCh[++newStartIdx]
        }
    }
    // 看看有没有剩余的, 循环结束了start还是比old小
    if (newStartIdx <= newEndIdx) {
        console.log('new还有剩余节点没有处理,要添加项')
        // 插入的标杆
        const before = newCh[newEndIdx + 1] == null ? null : newCh[newEndIdx + 1].elm
        // 遍历新的newCh，添加到老的没有处理的之前
        console.log('before',before)
        for (let i = newStartIdx; i <= newEndIdx; i++) {
            console.log(oldStartIdx)
            // insertBefore方法可以自动识别null，如果是null就会自动排到队尾，和appendChild一致
            // newch[i] 现在还没有变成真正的DOM，所以要调用createElement使其变为DOM
            // parentElm.insertBefore(createElement(newCh[i]), before)
            parentElm.insertBefore(createElement(newCh[i]), oldCh[oldStartIdx].elm)
        }
    } else if (oldStartIdx <= oldEndIdx) {
        console.log('old还有剩余节点没有处理,要删除项')
        // 批量删除oldStart和oldEnd指针之间的项
        for (let i = oldStartIdx; i <= oldEndIdx; i++) {
            if (oldCh[i]) {
                parentElm.removeChild(oldCh[i].elm)
            }
        }
    }
}